#include <SingletonHolder.h>
#include <PFileInfo.h>
#include <vector>
#include <boost/date_time.hpp>
#include <boost/any.hpp>
#include <Tools.h>

//project headers
#include "TaskConvert.h"
#include "../Plugin/IConverter.h"
#include "../Client/Client.h"


namespace Tasks{

//! Struktura Row_PracaStudenta
/*!
	Pomocna trieda ktora reprezentuje jeden riadok v tabulke
	praca_studenta.
*/
typedef struct Row_PracaStudenta
{
	unsigned int id;					//nepouziva sa pre insert
	std::string student_id;				//maximalna velkost loginu je 8 znakov
	std::string predmet;
	std::string zadanie_original;
	std::string zadanie_html;
	std::wstring zadanie_txt;			//kodovane do UTF-8
	std::string datum_odovzdavania;		//format datumu ma mat ISO tvar YYYY-MM-DD priklad ("2010-01-31")
	double		hodnotenie;
	std::string ohodnotil_id;
	bool		podrobene_kontrole;
	std::string meno_studenta;			//predstavuje meno a priezvisko studenta
	std::string semester;

	DB::Datapair GetRow()
	{
		DB::Datapair aRow;

		//naplnime kolekciu
		aRow.push_back(std::make_pair<std::string,boost::any>("id_studenta",student_id));
		aRow.push_back(std::make_pair<std::string,boost::any>("predmet",predmet));
		aRow.push_back(std::make_pair<std::string,boost::any>("praca_original",zadanie_original));
		aRow.push_back(std::make_pair<std::string,boost::any>("praca_html",zadanie_html));
		aRow.push_back(std::make_pair<std::string,boost::any>("praca_text",zadanie_txt));
		aRow.push_back(std::make_pair<std::string,boost::any>("datum_odovzdania",datum_odovzdavania));
		//aRow.push_back(std::make_pair<std::string,boost::any>("hodnotenie",hodnotenie));	//predbezne vynechavame aby hodnota bola NULL
		//aRow.push_back(std::make_pair<std::string,boost::any>("id_ohodnotil",ohodnotil_id));
		aRow.push_back(std::make_pair<std::string,boost::any>("podrobene_kontrole",podrobene_kontrole));
		aRow.push_back(std::make_pair<std::string,boost::any>("meno_studenta",meno_studenta));
		aRow.push_back(std::make_pair<std::string,boost::any>("semester",semester));

		return aRow;
	}

}Row_PracaStudenta;


TaskConvert::TaskConvert(DB::IDatabase *ptheDBConnection,ConvertorManager *ptheConvertorsMgr)
{
	my_pDBConnection = ptheDBConnection;
	my_pConvertorMgr = ptheConvertorsMgr;

	myClientPort = -1;

}

TaskConvert::TaskConvert(DB::IDatabase *ptheDBConnection, ConvertorManager *ptheConvertorsMgr, std::string theClientAddress,std::string theClientPort)
{
	my_pDBConnection = ptheDBConnection;
	my_pConvertorMgr = ptheConvertorsMgr;
	myClientAddress = theClientAddress;
	myClientPort = theClientPort;
}

void TaskConvert::operator()()
{
	myData.theConnection->close();

	Row_PracaStudenta _tmpRow;

	std::vector<std::string> _stringData = myData.theContainer.GetContainer<std::string>(); // ziskame vsetky data typu string

	//najprv skontrolujeme ci vsetky data boli poslane alebo ne
	if(_stringData.size() != 5)		//posiela sa username(id studenta), realname, predmet, cesta k zip
	{
		SingletonHolder::Instance().WriteEx("TaskConvert:	Wrong or demaged DataContainer received from modul: Vstupny!");
		return;
	}

	//naplnime riadok tabulky informaciami ktore vieme hned na zaciatku
	_tmpRow.student_id = _stringData[0];		//dohoda je ze prvy udaj je id studenta
	_tmpRow.predmet = _stringData[2];
	_tmpRow.zadanie_original = _stringData[4];
	_tmpRow.datum_odovzdavania = get_date();
	_tmpRow.hodnotenie = 0.0f;
	_tmpRow.podrobene_kontrole = false;
	_tmpRow.meno_studenta = _stringData[1];
	_tmpRow.semester = _stringData[3];

	//ziskame vsetky subory na konverziu
	std::vector<PComunicator::PFileInfo> _filesToConvert = myData.theContainer.GetContainer<PComunicator::PFileInfo>();
	if(_filesToConvert.size() < 1)		//ak nemame ziadne subory, tak jeto zle
	{
		SingletonHolder::Instance().WriteEx("TaskConvert:	Wrong or demaged(no files to convert) DataContainer received from modul: Vstupny!");
		return;
	}

	std::string _pathToSave =  Tools::GetPath(_stringData[4]);		//ziskame cestu kam budeme ukladat web stranky

	/*
	 * Zvlast kazdy subor sa prekonvertuje a pre kazdy prekonvertovany
	 * subor sa vytvory jeden riadok ktory je nasledne pridany do DB.
	 * V pripade ak sa podary naviazat spojenie s porovnavacim modulom
	 * tak tomuto modulu sa poslu primarne kluce novych prekonvertovanych
	 * dokumentov.
	 */

	// --LOCK--------------------------------------------------------
	boost::mutex::scoped_lock lock(myMutex);
	for(std::size_t i=0; i < _filesToConvert.size(); i++)
	{
		//vyberieme si konverter na zaklade formatu dokumentu
		PluginSystem::IConverter *p_converter = my_pConvertorMgr->Get(_filesToConvert[i].GetFileType());
		if(p_converter == NULL)
		{
			SingletonHolder::Instance().WriteEx("TaskConvert:	No converter found for document:" + _filesToConvert[i].GetName());
			continue;
		}

		if(p_converter->Open(_filesToConvert[i]))
		{
			Row_PracaStudenta _rowcopy = _tmpRow;		//urobime kopiu zakladnych info
			_rowcopy.zadanie_txt = p_converter->Read();
			_rowcopy.zadanie_html = p_converter->CreateHtml(_pathToSave);  //prekonvertujeme do html a ulozime cestu k html
			
			kill_newlines(_rowcopy.zadanie_txt);

			std::string tableName = "praca_studenta";
			DB::Datapair _theRow = _rowcopy.GetRow();
			if(my_pDBConnection->Insert(tableName,_theRow))
			{
				SingletonHolder::Instance().WriteMsg("TaskConverter: Document:"+_filesToConvert[i].GetName() +" from: "+_rowcopy.student_id+" sucessfuly converted.");
				//ak bol uspesny insert tak posleme primarny kluc pre porovnavaci modul
				send_pk();
			}

		}
		else
		{
			SingletonHolder::Instance().WriteEx("TaskConvert:	Cant open document:" + _filesToConvert[i].GetName());
			continue;
		}
	}
	// --END OF LOCK--------------------------------------------------
}

std::string TaskConvert::get_date()
{
	boost::gregorian::date date = boost::gregorian::day_clock::local_day();
	return boost::gregorian::to_sql_string(date);
}

void TaskConvert::send_pk()
{
	if(myClientAddress.empty() || myClientPort.empty()) //nesu dostupne data nic neposielame :)
		return;

	try
	{
		SingletonHolder::Instance().WriteMsg("Preparing sending data tu porovnavaci modul\nTo Address: "+myClientAddress +"\nAnd port : "+myClientPort);
		Client _client(myClientAddress,myClientPort);
		PComunicator::DataContainer _container;
		_container.AddCommand("newkey",false);

		_client.Connect();
		_client.Write(_container);
	}
	catch (...)
	{
		SingletonHolder::Instance().WriteEx("TaskConvert:	Sending data to modul porovnavaci failed.");
	}
}

void TaskConvert::kill_newlines(std::wstring &input)
{
	for(std::size_t i=0; i < input.size(); i++)
	{
		if(input[i] == '\n' || input[i] == '\r' || input[i] == '\t')
		{
			input[i] = ' ';
		}
	}
	
	std::wstring::iterator iter = input.begin();
	bool isLastSpace = false;

	for(; iter != input.end()-1; iter++)
	{
		if((*iter) == ' ')
		{	
				if(isLastSpace)
				{
					input.erase(--iter);
				}	
			isLastSpace = true;
		}
		else
		{
			isLastSpace = false;
		}
	}
}

}//end of Tasks namespace
